package org.zoomdev.zoom.dao;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.zoomdev.zoom.dao.ddl.DatabaseBuilder;
import org.zoomdev.zoom.dao.ddl.DatabaseBuilderImpl;
import org.zoomdev.zoom.dao.driver.SqlDriver;
import org.zoomdev.zoom.dao.entity.Entity;
import org.zoomdev.zoom.dao.entity.EntityBuilder;
import org.zoomdev.zoom.dao.entity.EntityFactory;
import org.zoomdev.zoom.dao.modules.CoreDaoModule;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;

public final class Dao {

    private static final Logger log = LoggerFactory.getLogger(CoreDaoModule.NAME);
    private final EntityFactory entityFactory;
    private final DataSource dataSource;

    private SqlDriver driver;
    private String productName;
    private String url;

    public Dao(DataSource dataSource) {
        this.dataSource = dataSource;
        this.entityFactory = new EntityFactory(this);
        load();
    }

    public DatabaseBuilder databaseBuilder() {
        return new DatabaseBuilderImpl(dataSource);
    }

    public void registerSplitTableRule(String column, SplitTableRule rule) {
        entityFactory.registerSplitRule(column, rule);
    }

    public <T> Ar<T> ar(Class<T> classOfT) {
        ActiveRecord<T> ar = ar();
        ar.setEntity(entityFactory.getEntity(classOfT));
        return ar;
    }

    public <T> ActiveRecord<T> ar() {
        ActiveRecord<T> ar = ActiveRecord.ar();
        ar.setDao(this);
        return ar;
    }

    public <T> Ar<T> ar(Class<T> classOfT, String table) {
        ActiveRecord<T> ar = ar();
        ar.setEntity(entityFactory.getEntity(classOfT, table));
        return ar;
    }

    public Ar<Record> ar(String table) {
        ActiveRecord<Record> ar = ar();
        ar.setEntity(getEntity(table));
        return ar;
    }

    public Entity getEntity(String table) {
        return entityFactory.getEntity(null, table);
    }

    public <T> Ar<T> ar(Entity<T> entity) {
        ActiveRecord<T> ar = ar();
        ar.setEntity(entity);
        return ar;
    }

    public EntityBuilder entityBuilder() {
        return new EntityBuilder(entityFactory);
    }
//
//
//    /**
//     * 直接修改
//     *
//     * @param sql
//     * @param args
//     * @return
//     */
//    public int executeUpdate(String sql, Object... args) {
//        Connection conn = null;
//        Ar<Object> ar = ar();
//        try {
//            conn = ar.openConnection();
//            try (PreparedStatement ps = conn.prepareStatement(sql)) {
//                int index = 1;
//                for (Object arg : args) {
//                    ps.setObject(index++, arg);
//                }
//                return ps.executeUpdate();
//            }
//        } catch (SQLException e) {
//            throw new DaoException(e);
//        } finally {
//            ar.releaseConnection(conn);
//        }
//    }
//
//
//    /**
//     * 数量等统计值
//     *
//     * @param type
//     * @param sql
//     * @param args
//     * @param <T>
//     * @return
//     */
//    public <T> T getValue(Class<T> type, String sql, Object... args) {
//        Connection conn = null;
//        Ar<Object> ar = ar();
//        try {
//            conn = ar.openConnection();
//            try (PreparedStatement ps = conn.prepareStatement(sql)) {
//                int index = 1;
//                for (Object arg : args) {
//                    ps.setObject(index++, arg);
//                }
//                try (ResultSet rs = ps.executeQuery()) {
//                    if (rs.next()) {
//                        return rs.getObject(1, type);
//                    }
//                    return null;
//                }
//            }
//        } catch (SQLException e) {
//            throw new DaoException(e);
//        } finally {
//            ar.releaseConnection(conn);
//        }
//    }
//
//    /**
//     * 适用于统计
//     *
//     * @param sql
//     * @param args
//     * @return
//     */
//    public Values getValues(String sql, Object... args) {
//        Connection conn = null;
//        Ar<Object> ar = ar();
//        try {
//            conn = ar.openConnection();
//            try (PreparedStatement ps = conn.prepareStatement(sql)) {
//                int index = 1;
//                for (Object arg : args) {
//                    ps.setObject(index++, arg);
//                }
//                try (ResultSet rs = ps.executeQuery()) {
//                    if (rs.next()) {
//                        ResultSetMetaData md = rs.getMetaData();
//                        int columnCount = md.getColumnCount();
//                        List<Object> data = new ArrayList<>();
//                        for (int i = 0; i < columnCount; ++i) {
//                            data.add(rs.getObject(i + 1));
//                        }
//                        return new Values(data);
//                    }
//                    return null;
//                }
//            }
//        } catch (SQLException e) {
//            throw new DaoException(e);
//        } finally {
//            ar.releaseConnection(conn);
//        }
//    }
//
//    /**
//     * 注意，并不会检查字段的一一对应
//     * <p>
//     * class Order{
//     * Integer id;
//     * String proName;
//     * String merName;
//     * }
//     * <p>
//     * executeQuery(Order.class,"select id,pro_name,mer_name from order where id=?",1);
//     * or
//     * executeQuery(Order.class,"select * from order where id=?",1);
//     * <p>
//     * <p>
//     * class Order{
//     * Integer id;
//     * String name;
//     * String mer;
//     * }
//     * executeQuery(Order.class,"select pro_id as id,pro_name as name,mer_name as mer from order where id=?",1);
//     * <p>
//     * class Statistics{
//     * int count;
//     * BigDecimal amt;
//     * }
//     * <p>
//     * executeQuery(Statistics.class,"select count(*) as count,sum(amt) as amt from order")
//     * .stream().findFirst().get();
//     *
//     * @param type
//     * @param sql
//     * @param args
//     * @param <T>
//     * @return
//     */
//    @SuppressWarnings("unchecked")
//    public <T> List<T> executeQuery(Class<T> type, String sql, Object... args) {
//        ValueCaster caster = Caster.wrap(Record.class, type);
//        return executeQuery(sql, args).stream().map(i -> (T) caster.to(i))
//                .collect(Collectors.toList());
//    }
//
//    public List<Record> executeQuery(String sql, Object... args) {
//        Connection conn = null;
//        Ar<Object> ar = ar();
//        try {
//            conn = ar.openConnection();
//            try (PreparedStatement ps = conn.prepareStatement(sql)) {
//                int index = 1;
//                for (Object arg : args) {
//                    ps.setObject(index++, arg);
//                }
//                try (ResultSet rs = ps.executeQuery()) {
//                    List<Record> result = new ArrayList<>();
//                    ResultSetMetaData rsmd = rs.getMetaData();
//                    int columnCount = rsmd.getColumnCount();
//                    String[] labelNames = new String[columnCount + 1];
//                    ResultSetAdapter[] types = new ResultSetAdapter[columnCount + 1];
//                    for (int i = 1; i < labelNames.length; i++) {
//                        labelNames[i] = NameCaseConverter.toCamelCase(rsmd.getColumnLabel(i));
//                        types[i] = ResultSetAdapters.create(rsmd.getColumnType(i));
//                    }
//                    while (rs.next()) {
//                        Record record = new Record();
//                        for (int i = 1; i <= columnCount; i++) {
//                            record.put(labelNames[i], types[i].getObject(rs, i));
//                        }
//                        result.add(record);
//                    }
//                    return result;
//                }
//            }
//        } catch (SQLException e) {
//            throw new DaoException(e);
//        } finally {
//            ar.releaseConnection(conn);
//        }
//    }

    public DataSource getDataSource() {
        return dataSource;
    }

    public SqlDriver getDriver() {
        return driver;
    }

    public void setDriver(SqlDriver driver) {
        this.driver = driver;
    }

    public String getProductName() {
        return productName;
    }

    public String getUrl() {
        return url;
    }

    public EntityFactory getEntityFactory() {
        return entityFactory;
    }

    private void load() {
        // 需要绑定entities
        try (Connection connection = dataSource.getConnection()) {
            DatabaseMetaData metaData = connection.getMetaData();
            String name = metaData.getDatabaseProductName();
            log.info("Database type : {}", name);
            this.url = metaData.getURL();
            this.driver = SqlDriver.create(name);
            this.productName = name;
        } catch (SQLException e) {
            throw new DaoException("创建Dao失败,连接数据库错误", e);
        }
    }
}
